Proguard

Proguard 混淆详细规则

code 托管:<br /

git@git.oschina.net:zengfansheng/ProguardDemo.git

Proguard 语法

http://proguard.sourceforge.net/index.html#manual/usage.html
或者:
https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/usage.html
android 官网:
https://developer.android.com/studio/build/shrink-code.html
demo 托管:
http://git.oschina.net/zengfansheng/ProguardDemo

Shrinking Options 压缩

默认开启,用以减小应用体积,移除未被使用的类、变量、方法和属性,并且会在优化动作执行之后再次执行(因
为优化后可能会再次暴露一些未被使用的类和成员)。

Optimization Options 优化

默认开启,在字节码级别执行优化,让应用运行的更快。非入口节点类加上 private/static/final,没有用到的参数会被删除,一些方法可能会变成内联代码

Obfuscation Options 混淆

默认开启,增大反编译难度,类和类成员会被随机命名,除非用 keep 保护。使用短又没有语义的名字重命名非入口类的类名,变量名,方法名。入口类的名字保持不变。

Preverification Options 预校验

预校验代码是否符合 Java1.6 或者更高的规范 (唯一一个与入口类不相关的步骤)

其他

General Options

Input/Output Options

-libraryjars libs/gson-2.2.4.jar

Keep Options

# 保留Student的类名
-keep public class com.example.fragmentdemo.Student
# 保留Student类名及其get和set方法,但settergetter字段名没有保存
-keep public class com.example.fragmentdemo.Student {
    *** get*();
    void set*(***);
}
# 不混淆自定义View的构造方法和set方法
-keepclassmembers class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}
# 不混淆com.baidu.batsdk包下所有类的成员
-keepclassmembers public class com.baidu.batsdk.*
{
    *;
}

Keep Option Modifiers

Proguard 基本规则

***


-keep class cn.hadcn.test.** // keep住该包下及子包下的类名不被混淆

-keep class cn.hadcn.test.* // keep住该包下的类的类名不混淆,但子包下的类的类名会被混淆

一颗星表示 只是保持该包下的类名,而子包下的类名还是会被混淆;两颗星表示把 本包和所含子包下的类名都保持

用以上方法保持类后,你会发现类名虽然未混淆,但里面的具体方法和变量命名还是变了,这时如果既想保持类名,又想保持里面的内容不被混淆,我们就需要以下方法了:


-keep class cn.hadcn.test.* {*;} // keep该包下的类的类名和类的属性方法不被混淆,子包下的会被混淆

也可以使用 Java 的基本规则来保护特定类不被混淆,比如我们可以用 extend,implement 等这些 Java 规则。如下例子就避免所有继承 Activity 的类被混淆


# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆 

# 因为这些子类都有可能被外部调用 

-keep public class * extends android.app.Activity 

-keep public class * extends android.app.Appliction 

-keep public class * extends android.app.Service 

-keep public class * extends android.content.BroadcastReceiver 

-keep public class * extends android.content.ContentProvider 

-keep public class * extends android.app.backup.BackupAgentHelper 

-keep public class * extends android.preference.Preference 

-keep public class * extends android.view.View 

-keep public class com.android.vending.licensing.ILicensingService

保留一个类中的内部类不被混淆则需要用 $ 符号:


// 保持ScriptFragment内部类JavaScriptInterface中的所有public内容不被混淆。

-keepclassmembers class cc.ninty.chat.ui.fragment.ScriptFragment$JavaScriptInterface { 

 public *; 

}

一个类中你不希望保持全部内容不被混淆,而只是希望保护类下的特定内容,就可以使用:


<init>(参数类型); // 匹配所有构造器 

<fields>(); // 匹配所有域 

<methods>(参数类型); // 匹配所有方法方法

还可以在 <fields><methods> 前面加上 private 、public、native 等来进一步指定不被混淆的内容:


-keep class cn.hadcn.test.One { 

 public <methods>; 

}



// One类下的所有public方法都不会被混淆,当然你还可以加入参数,比如以下表示用JSONObject作为入参的构造函数不会被混淆

-keep class cn.hadcn.test.One { 

 public <init>(org.json.JSONObject); 

}

keep 及其他 keepxxx

保留 防止被移除或者被重命名 防止被重命名
类和类成员 -keep -keepnames
仅类成员 -keepclassmembers -keepclassmembernames
如果拥有某成员,保留类和类成员 -keepclasseswithmembers -keepclasseswithmembernames

移除是指在压缩 (Shrinking) 时是否会被删除

ProGuard 规则中最常用的是 Keep 选项,所以下面主要讲 Keep 选项的语法,ProGuard 中有 6 组 Keep 选项,以表格的方式显示如下:
p8y0h
这六组选项看起来很容易搞混,其实只要掌握其中的关键差别就好区分了:

  1. 带 names 后缀的表示只是防止被混淆,还是可被移除,而没有 names 后缀则表示防止被混淆或移除;
  2. classmembers 表示只对类中成员生效,而 classeswithmembers 表示不仅对类中成员生效,还对包含它的类生效;
  3. keep 表示对类和类中匹配的成员生效,而 keepclasseswithmembers 表示对匹配的类成员及包含它的类生效,例如 -keep class * { native <methods>; } 表示保留所有的类和类中的 native 方法,-keepclasseswithmembers class * { native <methods>; } 表示保留所有的 native 方法及包含 native 方法的类。

通配符

tucgm
注意:

在了解 Keep 选项和通配符后,在回头看默认的 proguard-android.txt 文件就轻松多了。

Proguard 不混淆字段、方法

# keep注解
-keep @com.pandaq.appcore.framework.annotation.ProguardKeep class * {*;}
# keep注解修饰的字段
-keep class * {
    @com.pandaq.appcore.framework.annotation.ProguardKeep <fields>; 
}
# keep注解修饰的方法
-keepclassmembers class * {
    @com.pandaq.appcore.framework.annotation.ProguardKeep <methods>;
}

consumerProguardFiles- 在 library 中提供 Proguard 规则

第三方库的作者们也有义务向您提供必要的混淆规则配置来避免开启 Proguard 导致的构建失败或者应用崩溃。
有些项目简单地在他们的文档或者 README 上提及了必要的混淆规则,所以您需要复制粘贴这些规则到您的主 ProGuard 配置文件中。不过有个更好的方法,第三方库的维护者们如果发布的库是 AAR ,那么可以指定规则打包在 AAR 中并会在应用构建时自动暴露给构建系统,通过添加下面几行代码到库模块的 build.gradle 文件中:

release { //or your own build type  
  consumerProguardFiles ‘consumer-proguard.txt’  
}

您写入在 consumer-proguard.txt 文件中的规则将会在应用构建时附加到应用主 ProGuard 配置并被使用。

混淆规则的叠加

所有 module 的混淆规则会叠加。
如果想查看所有规则 叠加后的混淆规则,可在主目录的 proguard-rules.pro 添加下述配置:

# 输出所有规则叠加后的混淆规则
-printconfiguration ./build/outputs/mapping/full-proguard-rules.txt

Android Studio 中默认的 proguard-android.txt 和 proguard-android-optimize.txt 区别

一般在 gradle 引入

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 获得官方自带的混淆规则文件路径,另一个是与当前 gradle 相同目录下的 proguard-rules.pro 文件路径。
// getDefaultProguardFile获取的是/build/intermediates/proguard-files

位于 /sdk/tools/proguard 有下面两个文件

  1. proguard-android.txt
  2. proguard-android-optimize.txt

差异对比:
hef7b

proguard-android.txt

这个文件,在 AGP2.2+ 不再使用了,AGP 会在 build/intermediates/proguard-files 生成 proguard 文件

proguard-android-optimize.txt

如果你不想 optimize,用 proguard-android.txt

Android 中不进行 Proguard 技巧

不被混淆的情况

  1. 反射用到的类不混淆 (否则反射可能出现问题)
  2. 与服务端交互时,使用 GSON、fastjson 等框架解析服务端数据时,所写的 JSON 对象类不混淆,否则无法将 JSON 解析成对应的对象;
  3. JNI 方法 (native 方法),jni 方法不可混淆,因为这个方法需要和 native 方法保持一致;
-keepclasseswithmembernames class * {  
  # 保持native方法不被混淆 
  native <methods>; 
}
  1. AndroidMainfest 中的类不混淆,所以四大组件和 Application 的子类和 Framework 层下所有的类默认不会进行混淆。自定义的 View 默认也不会被混淆;所以像网上贴的很多排除自定义 View,或四大组件被混淆的规则在 Android Studio 中是无需加入的;
  2. 有用到 WebView 的 JS 调用也需要保证写的接口方法不混淆,原因同 jni 方法一样;
  3. Parcelable 的子类和 Creator 静态成员变量不混淆,否则会产生 Android.os.BadParcelableException 异常;
# 保持Parcelable不被混淆 
-keep class * implements Android.os.Parcelable { 
 public static final Android.os.Parcelable$Creator *; 
}
  1. 使用 enum 类型时需要注意避免以下两个方法混淆,因为 enum 类的特殊性,以下两个方法会被反射调用,见反射规则
-keepclassmembers enum * { 
 public static **[] values(); 
 public static ** valueOf(java.lang.String); }
  1. 使用第三方开源库或者引用其他第三方的 SDK 包时,如果有特别要求,也需要在混淆文件中加入对应的混淆规则

5、Serializable 类

不混淆接口 INoProguard

定义接口

/**
 * 实现该接口不混淆
 * <p/>
 * Created by zengfansheng on 2016/5/25.
 */
public interface INoProguard {
}

混淆配置:

-keep public class com.baidu.browser.util.proguard.INoProguard
-keep class * implements com.baidu.browser.util.proguard.INoProguard {
    *;
}

缺点:

不混淆注解 NoProguard

定义不混淆注解标记

/**
 * 不进行混淆的注解标记位
 *
 *  NotProguard, Means not proguard something, like class, method, field<br/>
 */
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
public @interface NoProguard {
}

混淆规则:

# 注解类NoProguard
-dontskipnonpubliclibraryclassmembers
-printconfiguration
-keep,allowobfuscation @interface com.example.proguard.proguarddemo.noproguard.NoProguard
-keep @com.example.proguard.proguarddemo.noproguard.NoProguard class *
-keepclassmembers class * {
    @com.example.proguard.proguarddemo.noproguard.NoProguard *;
}

#-keep @com.example.proguard.proguarddemo.noproguard.NoProguard class * {*;}
#-keep class * {
#    @com.example.proguard.proguarddemo.noproguard.NoProguard <fields>;
# }
#-keepclassmembers class * {
#    @com.example.proguard.proguarddemo.noproguard.NoProguard <methods>;
# }
@Keep 注解

目前 @Keep 并没有起作用,该混淆的还是混淆了;Gradle 还不支持@Keep 混淆,Google 只是定义好了一个这种注解,并没有实现它,也就是说@Keep 目前只是一个空壳。这里我们来手动开启它,让它支持防止混淆,在你的 proguard.cfg 或 proguard-android.txt 配置文件里面加入以下代码:

#手动启用support keep注解
-dontskipnonpubliclibraryclassmembers
-printconfiguration
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmembers class * {
    @android.support.annotation.Keep *;
}

案例:

//防止混淆类
@Keep
public class Person {}

//防止混淆变量
@Keep
public String name;

//防止混淆方法
@Keep
public int getAge(){}

现在已经默认支持@Keep 的混淆规则了?

参考:
http://hanhailong.com/2015/12/28/Android进阶之ProGuard代码混淆/

Android 自定义资源保持规则之 tools 属性

指定要忽略的资源文件

用 shrinkResources true 开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在 res/raw/ 路径下创建一个 xml 文件,例如 keep.xml。
通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:


tools:keep 定义哪些资源需要被保留(资源之间用“,”隔开)

tools:discard 定义哪些资源需要被移除(资源之间用“,”隔开)

tools:shrinkMode 资源压缩模式,有两种取值strict和safe,默认为safe

safe 和 strict 的优化策略:
safe 可以简单理解为安全模式,它会尽最大努力检查代码中可能会使用到的资源进行保留,避免运行时错误。

如果你的代码调用 Resources.getIdentifier(),这就表示你的代码将根据动态生成的字符串查询资源名称。当你执行这一调用时,默认情况下资源压缩器会采取防御性行为,将所有具有匹配名称格式的资源标记为可能已使用,无法移除。
当代码中通过 Resources.getIdentifier() 用动态的字符串来获取并使用资源时,普通的资源引用检查就可能会有问题。例如,如下代码会导致所有以 "img_" 开头的资源都被标记为已使用。


String name = String.format("img_%1d", angle + 1);

res = getResources().getIdentifier(name, "drawable", getPackageName());

启用严格的检测

我们可以设置 tools:shrinkMode 为 strict 来开启严格模式,使只有确实被使用的资源被保留。
以上就是自定义资源保持规则相关的配置,举个例子:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2"
    tools:shrinkMode="strict"/>

通常情况下,resource shrinker 可以准确地确定资源使用。如果你使用 Resources.getIdentifier() 动态获取指定资源的 Id,在默认情况下,这样资源具有匹配名称的格式为潜在的使用,无法去除。

例如,下面的代码将导致所有 img_ 前缀的资源都无法去除。


String name = String.format("img_%1d", angle + 1);

res = getResources().getIdentifier(name, "drawable", getPackageName());

resource shrinker 也通过搜索代码中是否包含资源名来判断是否在 build 的时候删除。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"tools:shrinkMode="strict" />

在 resource 文件中指定 shrinkMode,你可以指定 Gradle 在处理该资源文件时候的方式,默认的值为 safe,你也可以将它指定为 strict(只会保留有明确引用的资源,以及处理被 tools:keep 和 tools:discard 标注的资源)

Reference

Proguard 进阶用法

反混淆 Proguard 之 retrace

Decoding Obfuscated Stack Traces

retrace.bat -verbose mapping.txt obfuscated_trace.txt

隐藏类名字符串,保留源文件和行号,方便调试 crash

将.class 信息中的类名重新定义为 "Proguard" 字符串

# 将.class信息中的类名重新定义为"Proguard"字符串,保留源文件名为"Proguard"字符串,而非原始的类名
-renamesourcefileattribute Proguard

保留源文件和行号,配合 renamesourcefileattribute 可以将类名隐藏掉

-keepattributes SourceFile,LineNumberTable

混淆字典

obfuscationdictionary

-obfuscationdictionary filename
指定一个文本文件用来生成混淆后的名字。默认情况下,混淆后的名字一般为 a,b,c 这种。通过使用 -obfuscationdictionary 配置的字典文件,可以使用一些非英文字符做为类名。成员变量名、方法名。字典文件中的空格,标点符号,重复的词,还有以 '#' 开头的行都会被忽略。需要注意的是添加了字典并不会显著提高混淆的效果,只不过是更不利与人类的阅读。正常的编译器会自动处理他们,并且输出出来的 jar 包也可以轻易的换个字典再重新混淆一次。最有用的做法一般是选择已经在类文件中存在的字符串做字典,这样可以稍微压缩包的体积。

查找了字典文件的格式:一行一个单词,空行忽略,重复忽略,使用 java 中的关键字作字典,混淆之后的代码更加不利于阅读

# 这里巧妙地使用java中的关键字作字典,混淆之后的代码更加不利于阅读
#
# This obfuscation dictionary contains reserved Java keywords. They can't
# be used in Java source files, but they can be used in compiled class files.
# Note that this hardly improves the obfuscation. Decent decompilers can
# automatically replace reserved keywords, and the effect can fairly simply be
# undone by obfuscating again with simpler names.
# Usage:
# java -jar proguard.jar ..... -obfuscationdictionary keywords.txt
#

do
if
for
int
new
try
byte
case
char
else
goto
long
this
void
break
catch
class
const
final
float
short
super
throw
while
double
import
native
public
return
static
switch
throws
boolean
default
extends
finally
package
private
abstract
continue
strictfp
volatile
interface
protected
transient
implements
instanceof
synchronized

混淆类名的字典

-classobfuscationdictionary filename

混淆包名的字典

-packageobfuscationdictionary filename

字典配置:

-obfuscationdictionary dictionary-drakeet.txt
-classobfuscationdictionary dictionary-drakeet.txt
-packageobfuscationdictionary dictionary-drakeet.txt

https://github.com/drakeet/proguard-dict
超级混淆字典:
https://github.com/Qrilee/AndroidObfuseDictionary

用 o0o0 这种来作为名字混淆

https://github.com/hqzxzwb/ProguardDictionaryGenerator

一种生成阅读极其困难的 proguard 字典的算法

-repackageclasses

这条规则配置特别强大,它可以把你的代码以及所使用到的各种第三方库代码统统移动到同一个包下,可能有人知道这条配置,但仅仅知道它还不能发挥它最大的作用,默认情况下,你只要在 rules 文件中写上 -repackageclasses 这一行代码就可以了,它会把上述的代码文件都移动到根包目录下,即在 / 包之下,这样当有人反编译了你的 APK,将会在根包之下看到 成千上万 的类文件并列着,除此之外,由于我们有时不得不 keep 一些类文件,于是你应用的包名层次仍然会存在,有一些没被完全混淆的类将继续存留在你的包名之下,这些类文件就相对得不到很好的保护。于是我要介绍一个小技巧,就是 -repackageclasses 后跟上一个你应用的包名,如:

-repackageclasses com.drakeet.purewriter.debug

最终 Proguard 会将包括第三方库的所有类文件都移动到你的包名之下,所谓 藏叶于林,这时候那些你未能完全混淆的类也可以藏身在这类文件大海之中,而且这些类文件名都会被混淆成 abcd 字母组合的名字。

需要注意的是,-repackageclasses + 你的包名 这种做法存在混淆 bug,而默认 -repackageclasses 不加包名不会出现 bug,所以初次使用此法需要进行测试,否则请退而求其次,关于这个 bug 的具体内容不多说,很赘述。

-overloadaggressively

混淆的时候大量使用重载,多个方法名使用同一个混淆名,但是他们的方法签名不同。这可以使包的体积减小一部分,也可以加大理解的难度。仅在混淆阶段有效。
注意,这项配置有一定的限制:

Sun的JDK1.2上会报异常 
Sun JRE 1.4上重载一些方法之后会导致序列化失败 
Sun JRE 1.5上pack200 tool重载一些类之后会报错 
java.lang.reflect.Proxy类不能处理重载的方法 
Google's Dalvik VM can't handle overloaded static fields(这句我不懂,重载静态变量是什么意思?有看懂的同学可以回复一下)

assumenosideeffects 移除 log 代码

混淆过程中删除 Log 代码
http://drakeet.me/android-advanced-proguard-and-security/#安全
用 Proguard 的 -assumenosideeffects 清除 log

log 正确使用姿势

  1. 禁用 System.out.println

Android 应用中,一般通过封装过的 Log 类来输出日志,方便控制。而 System.out.println 是标准的 Java 输出方法,使用不当,可能造成 Release 模式下输出日志的结果。

  1. 禁用 e.printStackTrace

禁用理由同上
建议通过封装过的 Log 类来输出异常堆栈信息

  1. Debug 模式下,通过一个静态变量,控制日志的显示隐藏。

我一般习惯直接使用 BuildConfig.DEBUG,当然,你也可以自己定义一个。

proguard 中移除 log 代码

在 Proguard 配置文件中,确保没有添加 --dontoptimize 选项 来禁用优化的前提下,


-assumenosideeffects class android.util.Log {   

    public static *** d(...);       

    public static *** e(...);    

    public static *** i(...);    

    public static *** v(...);   

    public static *** println(...);

    public static *** w(...);     

    public static *** wtf(...);

}

封装过的 Log 工具类调用日志使用上面的配置是否也可以被移除?
不会的。
需要添加自己的 LogUtil 配置

-assumenosideeffects class com.qiushibaike.common.log.LogUtil {

    public static *** v(...);

    public static *** d(...);

    public static *** i(...);

    public static *** w(...);

    public static *** e(...);

    public static *** wtf(...);

    public static *** json(...);

    public static *** xml(...);

    public static *** printStackTrace(...);
}

参考:
用 Proguard 的 -assumenosideeffects 清除 log
https://mp.weixin.qq.com/s/DE4gr8cTRQp2jQq3c6wGHQ

Proguard 注意

-libraryjars 和 -keep class 冲突

-libraryjars 和 -keep class 在 AndroidStudio 中不可以一起使用,否则用 Gradle 构建会提示 declared twice, 我是去掉 -libraryjars 解决的

Proguard 手册
ProGuard Manual
Proguard 遇到的问题
Troubleshooting

某些导致 Proguard 所有类名没有被混淆 (如 icepick)

icepick 库导致所有类没有被混淆,去掉该库的混淆规则就可以了

-keepnames class * { @icepick.State *;}

见 issues:
https://github.com/evernote/android-state/issues/15

Ref

第三方库 Proguard

https://github.com/krschultz/android-proguard-snippets

OkHttp3

https://github.com/square/okhttp/issues/2230#issuecomment-212822562

-keepattributes Signature
-keepattributes *Annotation*
# 前面两个可能已经存在
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**

Glide

https://github.com/bumptech/glide

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

Gson

https://zybuluo.com/wangwangheng/note/107971#7常用忽略第三方 jar 包语句>

-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.examples.android.model.** { *; }

Volley

-keep class com.android.volley.** {*;}
-keep class com.android.volley.toolbox.** {*;}
-keep class com.android.volley.Response$* { *; }
-keep class com.android.volley.Request$* { *; }
-keep class com.android.volley.RequestQueue$* { *; }
-keep class com.android.volley.toolbox.HurlStack$* { *; }
-keep class com.android.volley.toolbox.ImageLoader$* { *; }

DBFlow(待定)

https://github.com/Raizlabs/DBFlow/issues/248

-keep class com.raizlabs.android.dbflow.config.GeneratedDatabaseHolder

leakcanary


https://github.com/square/leakcanary/blob/master/leakcanary-android/consumer-proguard-rules.pro

-dontwarn com.squareup.haha.guava.**
-dontwarn com.squareup.haha.perflib.**
-dontwarn com.squareup.haha.trove.**
-dontwarn com.squareup.leakcanary.**
-keep class com.squareup.haha.** { *; }
-keep class com.squareup.leakcanary.** { *; }

# Marshmallow removed Notification.setLatestEventInfo()
-dontwarn android.app.Notification

stetho


https://github.com/facebook/stetho/tree/master/stetho-js-rhino#proguard
https://github.com/facebook/stetho/wiki/FAQ#general-issues

-keep class com.facebook.stetho.** { *; }

butterknife8


https://github.com/JakeWharton/butterknife/blob/master/butterknife/proguard-rules.txt

# Retain generated class which implement ViewBinder.
-keep public class * implements butterknife.internal.ViewBinder { public <init>(); }

# Prevent obfuscation of types which use ButterKnife annotations since the simple name
# is used to reflectively look up the generated ViewBinder.
-keep class butterknife.*
-keepclasseswithmembernames class * { @butterknife.* <methods>; }
-keepclasseswithmembernames class * { @butterknife.* <fields>; }

WebView 相关

# ---------------------------------webview相关------------------------------
# WebView(可选)
-keepclassmembers class * extends android.webkit.WebView {
   public *;
}

# WebView的复杂操作
-keepclassmembers class * extends android.webkit.WebViewClient {
     public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);
     public boolean *(android.webkit.WebView,java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient {
     public void *(android.webkit.WebView,java.lang.String);
}

# 与JS交互
-keepattributes SetJavaScriptEnabled
-keepattributes JavascriptInterface

# 保留与JS交互接口 , API17+
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}